//
// GatewayProtocolSession.java
//
// @author Michael Liljenstam, ISTS, Dartmouth College
//
// $Id: GatewayProtocolSession.java,v 1.2 2003/03/08 02:02:06 mili Exp $

package SSF.App.Worm;

import SSF.OS.*;
import SSF.Net.*;
import SSF.Net.Util.*;
import com.renesys.raceway.DML.*;
import SSF.Util.Random.*;
//import SSF.Util.Random.RandomStream;
import cern.jet.random.*;

import SSF.OS.BGP4.Util.*;

// ==== SSF.App.Worm.WormProtocolSession class ==========================/
/**
 * Pseudoprotocol for border routers (gateways). Connects to the global 
 * macroscopic model to receive information on worm induced scan traffic 
 * intensity.
 *
 * @author Michael Liljenstam, ISTS, Dartmouth College
 */
public class GatewayProtocolSession extends MacroscopicModelConfigurator 
    implements Configurable {

  /************************** Attributes ********************************/

  /** Current outgoing scan rate [scans/second]. */
  float              outgoingScanRate = 0;

  /** Fraction of IPv4 space routed (outgoing traffic) by this router. 
   *  That is fraction of space covered by routes in the routing table 
   *  of this router. If there's a default route this is 1.0 (the whole 
   *  space. Default value for this attribute is that the router has such a 
   *  default route.
   */
  float              routedSpaceFrac = 1.0F;

  /** Host entity where this pseudo-protocol is installed */
  Router             localRouter;

  /** Host's NHI address */
  String             localNHI;

  /** If true, this gateway blocks worm scan packets out from and into this AS.
   *  Other gateways for this AS may still be forwarding scans. */
  public boolean     scansBlocked = false;

  /** AS number this router belongs to. */
  int                asn;

  /** If non-null, send 'Blind Carbon Copies' of type 3 ICMPs to this IP 
   *  address. */
  String             bccIcmpsTo = null;

  /** Rate of BCC ICMPs to send. */
  float              bccIcmpRate = 0F;

  /** Inter-send time delay for bcc icmp sends. */
  Timer              bccSendTimer = null;

  /** Random number generator for BCC ICMP send process. */
  public RandomStream rng;

 /** Distribution of BCC ICMP inter-send times. */
  protected Exponential interSendTimeDistr = null;

  /** Print out debug information. */
  boolean            debug = false;

  //------------------------- Helper classes -----------------------------

  public class BccIcmpTransmissionTimer extends SSF.OS.Timer {
    GatewayProtocolSession owner = null;
      
    public BccIcmpTransmissionTimer(ProtocolGraph e, long dt, 
				    GatewayProtocolSession _owner) {
      super(e, dt);
      owner = _owner;
    }

    public void callback() {
      owner.sendBccIcmp();
      set();
    }
  }

  /************************** Methods    ********************************/

  public GatewayProtocolSession() {
    super();
    //    System.err.println("GatewayProtocolSession created");
  }

  /** 
   *  Initalize (after config phase). First instance creates global epidemic 
   *  model object and initiates the timer that drives the epidemic model. 
   *  All instances register with the epidemic model so that it can find them 
   *  deliver information about scan traffic intensity.
   */
  public void init() {
    // create worm model
    if (wormModel == null) {
	// create worm model
	init_macroscopic();
    }

    // register this router with the worm model
    if (!stratifiedOn) {
	wormModel.registerBorderRouter(this, 1); // set ASN=1 for all
    } else {
	// find out ASN
	asn = AS_descriptor.nh2as(AS_descriptor.get_as_nh((Host) inGraph()));
	// register
	wormModel.registerBorderRouter(this, asn);
    }

    // init rng
    rng = Net.accessRandomStream(this, this.name);
  }


  /** 
   *  Configure GatewayProtocolSession. 
   *  Also forwards global configuration of the macroscopic epidemic model to 
   *  MacroscopicModelConfigurator.
   *  <p>
   *  REQUIRED ATTRIBUTES: <br>
   *  <ul>
   *  </ul><p>
   *
   *  OPTIONAL ATTRIBUTES:<br> 
   *  <ul>
   *    <li><b>ip_space_routed</b> %F -- fraction of 2^32 bit IP space covered 
   *                                     by routes in this routers routing 
   *                                     table.
   *                                     If the router has a default route it 
   *                                     is always be 1.0. This is the default.
   *                                     For a default free routing table with 
   *                                     routes to all destinations, it is 
   *                                     typically around 0.4 ?? FIXME
   *
   *    <li><b>debug</b> %S           -- "debug true" will switch on debugging 
   *                                     printouts for this session. </li>
   *
   *    <li><b>bcc_icmps</b> %S       -- IP address to send blank carbon 
   *                                     copies of generated type 3 ICMPs to. 
   *                                     If absent no BCC ICMPs (default).
   *                                     This is a specific feature for DIB:s 
   *                                     test data generation. </li>
   *  </ul><p>
   *
   *  Whenever the <tt>WormProtocolSession</tt> or 
   *  <tt>GatewayProtocolSession</tt> is used, the 
   *  outermost <b>Net</b> <i>must have</i> a global definition of the 
   *  <b>worm_model [ ... ]</b> attribute. The <b>worm_model</b> attributes 
   *  are as follows:
   *
   *  @see MacroscopicModelConfigurator.configGlobalOptions() for more 
   *       details.
   */
  public void config(Configuration cfg) throws configException {
    super.config(cfg);

    //    System.err.println("WormProtocolSession config");

    localRouter = (SSF.Net.Router) inGraph();
    localNHI = localRouter.nhi;

    // configure global options
    try {
      // get to the top-level Net
      cidrBlock blk = localRouter.defined_in_network();
      while (blk.nhi_parent() != null) {
        blk = blk.nhi_parent();
      }
      Configuration globcfg =
            (Configuration)blk.networkConfiguration().findSingle("worm_model");
      configGlobalOptions(globcfg, (Configuration)blk.networkConfiguration());
    } catch (configException cex) {
      System.err.println("Problem configuring macroscopic worm model options");
      cex.printStackTrace();
    }

    // fraction of IP space routed (for outbound traffic)
    try {
      String routedSpaceFracStr = (String) cfg.findSingle("ip_space_routed");
      if (routedSpaceFracStr != null)
	  routedSpaceFrac = Float.parseFloat(routedSpaceFracStr);
    } catch (configException e) {
	System.err.println("GatewayProtocolSession: " + e);
    }

    // BCC of type 3 ICMPs 
    try {
      String bccIcmpsToStr = (String) cfg.findSingle("bcc_icmps");
      if (bccIcmpsToStr != null)
	  bccIcmpsTo = bccIcmpsToStr;
    } catch (configException e) {
	System.err.println("GatewayProtocolSession: " + e);
    }

    // look for debug flag
    try {
      if((cfg.findSingle("debug") != null) && 
	 (((String)cfg.findSingle("debug")).compareTo("true") == 0))
	  debug =  true;
    } catch (configException e) {
	System.err.println("GatewayProtocolSession: " + e);
    }

  } // config


  /**
   *  Do nothing.
   */
  public boolean push(ProtocolMessage msg, ProtocolSession fromSessn) {
    return true;
  }


  /**
   *  Update scan traffic intensity at this router.
   * 
   */
  public void updateScans(float outgoingRate) {
      outgoingScanRate = outgoingRate;

      if (debug) {
	System.err.println("t: " + getSimTime() 
			   + " Router " + localNHI 
			   + " outbound scan rate: " + outgoingScanRate 
			   + " [scans/s]");
      }

      if (bccIcmpsTo != null) {
	  setBccIcmpRate(outgoingRate);
      }
  }

  /**
   *  Set switch determining whether this gateway blocks (drops) worm 
   *  scan packets or not. By default all gateways forward scans. But e.g. a 
   *  gateway configured as a firewall or a crashed router might block the 
   *  worm scans.
   */
  public void setScansBlocked(boolean doBlock) {
      scansBlocked = doBlock;
  }

  /**
   *  Update the rate of BCC ICMPs to send. (One for each dropped scan 
   *  packet.)
   */
  public void setBccIcmpRate(float outgoingRate) {
      // Set send rate for ICMP BCCs for the next timestep.
      // what's not routed will be dropped here, with an ICMP
      bccIcmpRate = outgoingRate * (1 - routedSpaceFrac);
      interSendTimeDistr = new Exponential(bccIcmpRate, rng);

      // set timer
      if (bccIcmpRate > 0 && bccSendTimer == null) {
	  // sample next send time
	  double t = interSendTimeDistr.nextDouble();
	  if (debug) {
	      System.err.println("t: " + getSimTime() 
				 + " Router " + localNHI 
				 + " wait for " + t 
				 + " until next BCC ICMP send ");
	  }

	  if (debug) {
	      System.err.println("t: " + getSimTime() 
				 + " Router " + localNHI 
				 + " new BCC ICMP send timer");
	  }
          bccSendTimer = (new SSF.OS.Timer(localRouter, 
					   SSF.Net.Net.seconds(t)){
		  public void callback() {
		      sendBccIcmp();
		      // cancel();
		  }
	      });
	  bccSendTimer.set(SSF.Net.Net.seconds(t));
      }
  }

  /**
   * Send next BCC ICMP packet.
   * Queuing them according to a Poisson process.
   */
  public void sendBccIcmp() {
      IP ipSess;
      try {
	  ipSess = (IP) inGraph().SessionForName("ip");
      } catch (ProtocolException e) {
	  System.err.println("ERROR in GatewayProtocolSession: Can't find IP session");
	  e.printStackTrace();
	  return;
      }
      // get a 'typical' scan packet header
      IpHeader scanIp = MacroscopicModel.ref.wormTraffic.makeScanPacket();

      // create ICMP packet & send
      ICMPHeader icmpHdr = 
	  ICMPHeader.makeHostUnreachableMessage(scanIp);
      IpHeader pkt = (IpHeader) icmpHdr.previous();
      pkt.DEST_IP = IP_s.StrToInt(bccIcmpsTo);
      try {
	  ipSess.push(pkt, this);
      } catch (ProtocolException e) {
	  System.err.println("ERROR in GatewayProtocolSession: Couldn't send ICMP packet");
	  e.printStackTrace();
	  return;
      }
      if (debug) {
	System.err.println("t: " + getSimTime() 
			   + " Router " + localNHI 
			   + " sent BCC ICMP to " + bccIcmpsTo);
      }

      // sample next send time
      double t = interSendTimeDistr.nextDouble();
      /*      if (debug) {
	System.err.println("t: " + getSimTime() 
			   + " Router " + localNHI 
			   + " wait for " + t + " until next BCC ICMP send ");
			   } */
      bccSendTimer.set(SSF.Net.Net.seconds(t));
  }
}
